package com.ricky.onlineShop.shop.trading.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.ricky.admin.util.LoginUserUtils;
import com.ricky.admin.util.NumUtil;
import com.ricky.base.repository.BaseRepository;
import com.ricky.common.core.domain.model.LoginUser;
import com.ricky.common.utils.DateUtils;
import com.ricky.common.exception.TradingException;
import com.ricky.onlineShop.shop.trading.mapStruct.TradingStruct;
import com.ricky.onlineShop.shop.trading.pojo.TradingPerformance;
import com.ricky.onlineShop.shop.trading.pojo.Trading;
import com.ricky.onlineShop.shop.trading.pojo.TradingFile;
import com.ricky.onlineShop.shop.trading.pojo.TradingOrder;
import com.ricky.onlineShop.shop.trading.repository.TradingRepository;
import com.ricky.onlineShop.shop.trading.service.ITradingFileService;
import com.ricky.onlineShop.shop.trading.service.ITradingOrderService;
import com.ricky.onlineShop.shop.trading.service.ITradingPerformanceService;
import com.ricky.onlineShop.shop.trading.service.ITradingService;
import com.ricky.onlineShop.shop.trading.vo.TradingVo;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.mapstruct.factory.Mappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author staticlan
 * @Date 2024/6/20 15:42
 * @Description:
 */
@Service
@Slf4j
public class TradingServiceImpl implements ITradingService {
    @Autowired
    private TradingRepository tradingRepository;
    @Override
    public BaseRepository<Trading, String> getRepository() {
        return tradingRepository;
    }

    @Autowired
    private ITradingFileService tradingFileService;

    @Autowired
    private ITradingOrderService tradingOrderService;

    @Autowired
    private ITradingPerformanceService tradingPerformanceService;

    // 常量定义
    private static final String ORDER_TYPE_TRADE = "0";
    private static final String ORDER_STATUS_PENDING = "0";
    private static final String DATA_STATUS_ACTIVE = "1";

    // 用于并发控制的锁
    private final Lock lock = new ReentrantLock();

    /*
    * 添加网店信息
    * */
    @Override
    public Trading saveInfo(@NonNull TradingVo tradingVo) {
        // 1. 将转换和保存Trading的操作放在事务中
        Trading trading = Trading.toPojo(tradingVo);
        try {
            LoginUser user = LoginUserUtils.getLoginUser();
            trading.setShopHolder(user.getUser().getNickName());
            trading.setShopHolderId(user.getUserId().toString());
            String tradingNum = NumUtil.generateUniqueId();
            trading.setDataStatus("0");
            trading.setTradingNum(tradingNum);
            Trading save = tradingRepository.save(trading);
            List<TradingFile> shopFile = tradingVo.getShopPicture();
            // 2. 检查shopFile是否为空，避免空指针异常
            if (shopFile != null) {
                for (TradingFile tradingFile : shopFile) {
                    // 3. 在保存前设置必要的属性
                    tradingFile.setMainId(save.getId());
                    tradingFile.setFileDesc("网店图片");
                    // 4. 将保存TradingFile的操作也纳入事务中
                    tradingFileService.save(tradingFile);
                }
            }
            return save;
        } catch (Exception e) {
            // 5. 异常处理逻辑，可能需要记录日志或回滚事务
            // 根据实际情况处理异常，例如记录日志、回滚事务等
            throw new RuntimeException("保存交易信息失败", e);
        }
    }

    @Override
    public Trading updateTrading(@NonNull TradingVo tradingVo) {
        tradingVo.setUpdateTime(DateUtils.getNowDate());
        // 1. 将转换和保存Trading的操作放在事务中
        Trading trading = Trading.toPojo(tradingVo);
        try {
            Trading save = tradingRepository.save(trading);
            List<TradingFile> shopFile = tradingVo.getShopPicture();
            // 删除原来文件
            tradingFileService.deleteAllByMainId(trading.getId());
            // 2. 检查shopFile是否为空，避免空指针异常
            if (shopFile != null) {
                for (TradingFile tradingFile : shopFile) {
                    // 3. 在保存前设置必要的属性
                    tradingFile.setMainId(save.getId());
                    tradingFile.setFileDesc("网店图片");
                    // 4. 将保存TradingFile的操作也纳入事务中
                    tradingFileService.save(tradingFile);
                }
            }
            return save;
        } catch (Exception e) {
            // 5. 异常处理逻辑，可能需要记录日志或回滚事务
            // 根据实际情况处理异常，例如记录日志、回滚事务等
            throw new RuntimeException("保存交易信息失败", e);
        }
    }

    /*
    * 拍下订单的实现方法
    * */
    @Override
    public boolean tradingAuction(@NonNull String id) {
        try {
            Trading trading = findById(id);
            if (trading == null) {
                log.info("Trading not found for id: {}", id);
                throw new RuntimeException("找不到网站编号！");
            }
            // 有效性和边界条件校验
            if (!(trading.getSellingPrice().compareTo(BigDecimal.ZERO) >= 0 && trading.getHandPrice().compareTo(BigDecimal.ZERO) >= 0)) {
                log.warn("Invalid trading data: {}", trading);
                throw new RuntimeException("价格不能为负数，请联系管理员");
            }
            LoginUser user = LoginUserUtils.getLoginUser();
            TradingOrder tradingOrder = createTradingOrder(trading, user);
            TradingOrder order = tradingOrderService.save(tradingOrder);
            if (ObjectUtil.isEmpty(order)){
                log.error("创建订单失败 id: {}", id);
                throw new RuntimeException("创建订单失败 id: " + id);
            }
            trading.setDataStatus(DATA_STATUS_ACTIVE);
            tradingRepository.save(trading);
            return true;
        } catch (Exception e) {
            log.error("Failed to execute trading auction for id: {}", id, e);
            return false;
        }
    }

    /*
    * 封装订单信息
    * */
    private TradingOrder createTradingOrder(Trading trading, LoginUser user) {
        TradingOrder tradingOrder = new TradingOrder();
        tradingOrder.setOrderNo(NumUtil.generateUniqueId());
        tradingOrder.setOrderNo(NumUtil.generateUniqueId());
        tradingOrder.setTradingNum(trading.getTradingNum());
        tradingOrder.setTradingId(trading.getId());
        tradingOrder.setHandPrice(trading.getHandPrice());
        tradingOrder.setSellingPrice(trading.getSellingPrice());
        tradingOrder.setShopHolder(trading.getShopHolder());
        tradingOrder.setShopHolderId(trading.getShopHolderId());
        tradingOrder.setOrderHolder(user.getUser().getNickName());
        tradingOrder.setOrderHolderId(user.getUserId().toString());
        BigDecimal sellingPrice = trading.getSellingPrice();//网店出售价格
        BigDecimal handPrice = trading.getHandPrice();//卖家到手价格
        // 使用 BigDecimal 的 subtract 方法来计算差值  使用 max 方法来确保结果不会小于0
        BigDecimal performance = sellingPrice.subtract(handPrice).max(BigDecimal.ZERO);
        tradingOrder.setPerformance(performance);
        BigDecimal royalty = calculateRoyalty(performance, tradingOrder, user);//计算提成
        tradingOrder.setRoyalty(royalty);
        tradingOrder.setOrderType(ORDER_TYPE_TRADE);
        tradingOrder.setOrderStatus(ORDER_STATUS_PENDING);
        tradingOrder.setCompleteTime(System.currentTimeMillis());
        return tradingOrder;
    }

    /*
    * 提成计算方法
    * 1、订单提成：差价（业绩） * 25%
    * 2、网店所属人与订单所属人不相同则两人平分
    * */
    public BigDecimal calculateRoyalty(BigDecimal performance, TradingOrder tradingOrder, LoginUser user) {
        // Step 1: 空值校验
        if (tradingOrder == null || user == null) {
            throw new RuntimeException("订单信息和登陆用户不能为空。");
        }
        // Step 2: 业绩校验
        if (performance.compareTo(BigDecimal.ZERO) <= 0) {
            throw new RuntimeException("业绩必须大于0。");
        }

        // 使用try-with-resources确保锁的正确释放
        try {
            lock.lock();
            String loginUserId = user.getUserId().toString();
            String shopHolderId = tradingOrder.getShopHolderId();

            //添加绩效数据
            TradingPerformance tradingPerformancePojo = createTradingPerformance(tradingOrder, performance);
            BigDecimal royalty = performance.multiply(new BigDecimal("0.25"));  //计算提成
            if (!loginUserId.equals(shopHolderId)) {
                royalty = royalty.divide(new BigDecimal("2"));
                tradingPerformancePojo.setEmployee(tradingOrder.getShopHolder());
                tradingPerformancePojo.setEmployeeId(tradingOrder.getShopHolderId());
                tradingPerformancePojo.setRoyalty(royalty);
                tradingPerformanceService.save(tradingPerformancePojo);
            }
            TradingPerformance tpPojo = createTradingPerformance(tradingOrder, performance);
            tpPojo.setEmployee(tradingOrder.getOrderHolder());
            tpPojo.setEmployeeId(tradingOrder.getOrderHolderId());
            tpPojo.setRoyalty(royalty);
            tradingPerformanceService.save(tpPojo);
            return royalty;
        } finally {
            lock.unlock();
        }
    }

    private TradingPerformance createTradingPerformance(TradingOrder tradingOrder, BigDecimal performance) {
        TradingPerformance tradingPerformancePojo = new TradingPerformance();
        tradingPerformancePojo.setOrderId(tradingOrder.getId());
        tradingPerformancePojo.setOrderNo(tradingOrder.getOrderNo());
        tradingPerformancePojo.setTradingNum(tradingOrder.getTradingNum());
        tradingPerformancePojo.setTradingId(tradingOrder.getTradingId());
        tradingPerformancePojo.setPerformance(performance);
        return tradingPerformancePojo;
    }

    @Override
    public TradingVo selectTradingById(@NonNull String id) {
        Trading trading = getById(id);
        // 对潜在的null值进行检查
        if (trading == null) {
            throw new RuntimeException("未找到网店信息！");
        }
        List<TradingFile> tradingFiles;
        try {
            tradingFiles = tradingFileService.findByMainId(trading.getId());
        } catch (Exception e) {
            // 对可能的异常进行捕获并处理，这里简单地重新抛出运行时异常
            // 根据实际情况，这里可以进行日志记录、错误处理或其他恢复措施
            throw new RuntimeException("Failed to fetch trading files by main id", e);
        }
        //转换TradingVo
        TradingStruct tradingStruct = Mappers.getMapper(TradingStruct.class);
        TradingVo tradingVo = TradingVo.toVo(trading);
        tradingVo.setShopPicture(tradingFiles);
        return tradingVo;
    }
}
